/*
 * Copyright (c) 2017-present, CV4J Contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.cv4j.core.spatial.conv;

import com.cv4j.core.datamodel.ColorProcessor;
import com.cv4j.core.datamodel.ImageProcessor;
import com.cv4j.core.filters.BaseFilter;
import com.cv4j.image.util.Tools;

import java.util.Optional;

/**
 * laplas过滤器
 *
 * @author dev
 * @date 2021/07/22
 */
public class LaplasFilter extends BaseFilter {
    /**
     * 四个
     */
    public static int[] FOUR = new int[]{0, -1, 0, -1, 4, -1, 0, -1, 0};
    /**
     * 八个
     */
    public static int[] EIGHT = new int[]{-1, -1, -1, -1, 8, -1, -1, -1, -1};
    /**
     * 4个方向
     */
    private boolean _4direction;

    /**
     * laplas过滤器
     */
    public LaplasFilter() {
        _4direction = true;
    }

    /**
     * is4direct
     *
     * @return boolean
     */
    public boolean is4direct() {
        return _4direction;
    }

    /**
     * set4direct
     *
     * @param xdirect xdirect
     */
    public void set4direct(boolean xdirect) {
        this._4direction = xdirect;
    }

    /**
     * 做的过滤器
     *
     * @param src src
     * @return {@link Optional<ImageProcessor>}
     */
    @Override
    public Optional<ImageProcessor> doFilter(ImageProcessor src) {
        int total = width * height;
        byte[][] output = new byte[3][total];
        int offset = 0;
        int k0 = 0;
        int k1 = 0;
        int k2 = 0;
        int k3 = 0;
        int k4 = 0;
        int k5 = 0;
        int k6 = 0;
        int k7 = 0;
        int k8 = 0;
        if (_4direction) {
            k0 = FOUR[0];
            k1 = FOUR[1];
            k2 = FOUR[2];
            k3 = FOUR[3];
            k4 = FOUR[4];
            k5 = FOUR[5];
            k6 = FOUR[6];
            k7 = FOUR[7];
            k8 = FOUR[8];
        } else {
            k0 = EIGHT[0];
            k1 = EIGHT[1];
            k2 = EIGHT[2];
            k3 = EIGHT[3];
            k4 = EIGHT[4];
            k5 = EIGHT[5];
            k6 = EIGHT[6];
            k7 = EIGHT[7];
            k8 = EIGHT[8];
        }

        int sr = 0;
        int sg = 0;
        int sb = 0;
        int r1 = 0;
        int g1 = 0;
        int b1 = 0;
        for (int row = 1; row < height - 1; row++) {
            offset = row * width;
            for (int col = 1; col < width - 1; col++) {
                // red
                sr = k0 * ((RED[offset - width + col - 1] >> 16) & 0xff)
                    + k1 * (RED[offset - width + col] & 0xff)
                    + k2 * (RED[offset - width + col + 1] & 0xff)
                    + k3 * (RED[offset + col - 1] & 0xff)
                    + k4 * (RED[offset + col] & 0xff)
                    + k5 * (RED[offset + col + 1] & 0xff)
                    + k6 * (RED[offset + width + col - 1] & 0xff)
                    + k7 * (RED[offset + width + col] & 0xff)
                    + k8 * (RED[offset + width + col + 1] & 0xff);
                // green
                sg = k0 * (GREED[offset - width + col - 1] & 0xff)
                    + k1 * (GREED[offset - width + col] & 0xff)
                    + k2 * (GREED[offset - width + col + 1] & 0xff)
                    + k3 * (GREED[offset + col - 1] & 0xff)
                    + k4 * (GREED[offset + col] & 0xff)
                    + k5 * (GREED[offset + col + 1] & 0xff)
                    + k6 * (GREED[offset + width + col - 1] & 0xff)
                    + k7 * (GREED[offset + width + col] & 0xff)
                    + k8 * (GREED[offset + width + col + 1] & 0xff);
                // blue
                sb = k0 * (BLUE[offset - width + col - 1] & 0xff)
                    + k1 * (BLUE[offset - width + col] & 0xff)
                    + k2 * (BLUE[offset - width + col + 1] & 0xff)
                    + k3 * (BLUE[offset + col - 1] & 0xff)
                    + k4 * (BLUE[offset + col] & 0xff)
                    + k5 * (BLUE[offset + col + 1] & 0xff)
                    + k6 * (BLUE[offset + width + col - 1] & 0xff)
                    + k7 * (BLUE[offset + width + col] & 0xff)
                    + k8 * (BLUE[offset + width + col + 1] & 0xff);
                r1 = sr;
                g1 = sg;
                b1 = sb;

                output[0][offset + col] = (byte) Tools.clamp(r1);
                output[1][offset + col] = (byte) Tools.clamp(g1);
                output[2][offset + col] = (byte) Tools.clamp(b1);

                // for next pixel
                sr = 0;
                sg = 0;
                sb = 0;
            }
        }
        ((ColorProcessor) src).putRGB(output[0], output[1], output[2]);
        output = null;
        return Optional.ofNullable(src);
    }
}
